<div id="app"></div>
<style>
    .red {
        color: red;
    }

    .blue {
        color: blue;
    }
</style>
<script>
    function h(tag, props, children) {
        return {
            tag,
            props,
            children
        }
    }

    function mount(vnode, container) {
        const tag = vnode.tag;
        const el = vnode.el = document.createElement(tag); // 在vnode上添加el属性，用来存储原dom结构

        // props
        if (vnode.props) {
            for (let key in vnode.props) {
                let value = vnode.props[key];
                el.setAttribute(key, value);
            }
        }
        // children
        if (vnode.children) {
            if (typeof vnode.children === "string") {
                el.textContent = vnode.children;
            } else {
                vnode.children.forEach(child => {
                    mount(child, el);
                })
            }
        }

        container.appendChild(el);
    }
    // 将虚拟dom节点转为真实dom
    function createElm(vnode) {
        let {
            tag,
            props,
            children
        } = vnode;
        if (typeof tag === "string") {
            vnode.el = document.createElement(tag);
        }
        if (props) {
            for (let key in props) {
                let value = props[key];
                vnode.el.setAttribute(key, value);
            }
        }
        if (typeof vnode.children === "string") {
            vnode.el.textContent = vnode.children;
        } else {
            children.forEach(child => {
                return vnode.el.appendChild(createElm(child));
            })
        }
        return vnode.el;
    }
    // dom diff过程
    function patch(n1, n2) {
        if (n1.tag === n2.tag) {
            const el = n2.el = n1.el; //把n1的el真实dom内容，赋值给n2的el属性和单独的el对象上
            const oldProps = n1.props || {};
            const newProps = n2.props || {};
            for (const key in newProps) {
                const oldValue = oldProps[key];
                const newValue = newProps[key];
                if (newValue !== oldValue) {
                    el.setAttribute(key, newValue)
                }
            }
            // 处理新节点中不存在的属性,直接将属性移除
            for (const key in oldProps) {
                if (!(key in newProps)) {
                    el.removeAttribute(key)
                }
            }
            const oldChildren = n1.children;
            const newChildren = n2.children;
            if (typeof newChildren === "string") { // 新节点是字符串，直接删除旧节点，并使新接的文本
                if (typeof oldChildren === "string") {
                    if (newChildren !== oldChildren) {
                        el.textContent = newChildren
                    }
                } else { //旧节点不是字符串，说明包含多个子节点。同样也直接删除
                    el.textContent = newChildren
                }
            } else { //
                if (typeof oldChildren === "string") { //旧节点是字符串，新节点是多个子元素
                    el.innerHTML = '';
                    newChildren.forEach(child => {
                        mount(child, el)
                    })
                } else { //旧节点多个子元素，新节点多个子元素
                    // 找出新旧节点最长的共用长度
                    const commonLength = Math.min(oldChildren.length, newChildren.length);
                    // 比对公共长度的节点
                    for(let i = 0; i < commonLength; i++) {
                        patch(oldChildren[i], newChildren[i]);
                    }
                    // 如果新节点长度大于旧节点长度
                    if(newChildren.length > oldChildren.length){
                        newChildren.slice(oldChildren.length).forEach(child=>{
                            mount(child, el)
                        })
                    }
                    // 如果旧节点长度大于新节点长度
                    if(newChildren.length < oldChildren.length){
                        oldChildren.slice(newChildren).forEach(child=>{
                            el.removeChild(child.el)
                        })
                    }
                }
            }
        } else {
            // 直接替换replace
            n1.el.parentNode.replaceChild(createElm(vdom2), n1.el);
        }
    }

    const vdom = h('div', {
        class: 'red'
    }, [
        h('span', null, 'hello')
    ])
    const vdom2 = h('p', {
        class: 'blue'
    }, [
        h('span', null, 'changed'),
        h('p', {class: 'red'}, 'changed1'),
    ])
    // console.log(createElm(vdom2), 'dom2');
    mount(vdom, document.getElementById('app'))


    setTimeout(() => {
        patch(vdom, vdom2)
    }, 1000)
</script>